package com.autologistics.socket.service;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;


import com.autologistics.dao.MachineDao;
import com.autologistics.dao.MediaDao;
import com.autologistics.model.Machine;
import com.autologistics.model.Media;
import com.autologistics.socket.vo.FomatErrorRes;
import com.autologistics.socket.vo.MediaEnableCheckReq;
import com.autologistics.socket.vo.MediaFileConfirmReq;
import com.autologistics.socket.vo.MediaFileDeleteReq;
import com.autologistics.socket.vo.MediaFileUploadReq;
import com.autologistics.socket.vo.MediaTimeReq;
import com.autologistics.socket.vo.StateCheckReq;
import com.opensymphony.oscache.base.NeedsRefreshException;

import net.sf.json.JSONObject;

public class ServerRequest {

	private static ServerRequest instance = new ServerRequest();
	private HashMap<Integer, Integer> checkFailedTimes = new HashMap<>();
	public static ServerRequest getInstance(){
		return instance;
	}
	private boolean isrunning = false;
	private ServerRequest(){
		new Thread(){

			@Override
			public void run() {
				isrunning = false;
				while(isrunning){
					try {
						Thread.sleep(10000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					chekState();
				}
			}

		}.start();
	}

	public void destroy(){
		isrunning = false;
	}
	private class StateCheckThread extends Thread{

		private Machine machine;

		public StateCheckThread(Machine machine) {
			this.machine = machine;
		}

		@Override
		public void run() {
			LogAppender.appendLog("start a new thread to check one machine for "+machine.getId());
			String host = machine.getIpAddress();
			if(host == null){
				return;
			}

			try {
				final Socket soStateCheck = new Socket(host, 50000);
				StateCheckReq request = new StateCheckReq();
				final OutputStream osStateCheck = soStateCheck.getOutputStream();
				LogAppender.appendLog("send from server:"+JSONObject.fromObject(request).toString());
				osStateCheck.write(JSONObject.fromObject(request).toString().getBytes());
				//BufferedReader brCheck = new BufferedReader(new InputStreamReader(soStateCheck.getInputStream()));
				final InputStream is = soStateCheck.getInputStream();
				StringBuilder sb = new StringBuilder();
				//get response from machine
				new Thread(){

					@Override
					public void run() {
						// TODO Auto-generated method stub

						try {
							Thread.sleep(2000);
							is.close();
							osStateCheck.close();
							soStateCheck.close();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}

					}

				}.start();

				byte[] space = new byte[1024];
				int n =0;
				while((n=is.read(space)) == 1024){
					sb.append(new String(space,0,n));
				}
				sb.append(new String(space,0,n));

				if(checkFailedTimes.containsKey(machine.getId()) && machine.getState()==4){
					machine.setState(1);
					checkFailedTimes.put(machine.getId(), 1);
					((MachineDao) SpringContextWait.beanFcBeanFactory.getBean("machineDao")).updata(machine);
				}

				LogAppender.appendLog("recive from machine:"+JSONObject.fromObject(sb.toString()).toString());


				JSONObject jsonCheckRet = null;
				try {
					jsonCheckRet = JSONObject.fromObject(sb.toString());
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					jsonCheckRet = null;
				}

				if(jsonCheckRet == null){
					return;
				}

				if(jsonCheckRet.optInt("responseCode", -1)!=0){//not a success response
					return;
				}

				int totalBlank = jsonCheckRet.optInt("totalBlank", -1);
				int empyBlank = jsonCheckRet.optInt("empyBlank", -1);
				int usedBlank = jsonCheckRet.optInt("usedBlank", -1);
				int repairingBlank = jsonCheckRet.optInt("repairingBlank",-1);

				if(totalBlank == -1
						|| empyBlank == -1
						|| usedBlank == -1
						|| repairingBlank == -1){
					System.out.println("state check Threan parementer not complement");
					return ;
				}

				if(totalBlank <0
						|| empyBlank <0
						|| usedBlank <0
						|| repairingBlank <0){
					System.out.println("state check Thread parameter error,all value need be positive ");
					return;
				}

				if(totalBlank !=
						(empyBlank + usedBlank + repairingBlank)){
					System.out.println("state check Thread parameter error,total number not equals empty+usedBlank+repairingBlank number");
					return;
				}



				if(machine.getNumAll() != totalBlank
						|| machine.getNumRepair() != repairingBlank
						|| machine.getNumUsed() != usedBlank){//update numbers
					machine.setNumAll(totalBlank);
					machine.setNumRepair(repairingBlank);
					machine.setNumUsed(usedBlank);
					((MachineDao) SpringContextWait.beanFcBeanFactory.getBean("machineDao")).updata(machine);
				}


			} catch (UnknownHostException e) {
				// TODO Auto-generated catch block

				e.printStackTrace();
				LogAppender.appendLog("error when check state for machine:"+machine.getId()+"   "+machine.getIpAddress()+"  "+e.getMessage());
			}catch(java.net.ConnectException e){
				if(!checkFailedTimes.containsKey(machine.getId())){
					checkFailedTimes.put(machine.getId(), 1);
				}else{
					Integer testTimes = checkFailedTimes.get(machine.getId());
					checkFailedTimes.put(machine.getId(), testTimes+1);
					if(testTimes == 6){
						machine.setState(4);//for 4,define it offline
						((MachineDao) SpringContextWait.beanFcBeanFactory.getBean("machineDao")).updata(machine);
					}
				}
				e.printStackTrace();
				LogAppender.appendLog("machine offline:"+machine.getId()+"   "+machine.getIpAddress()+"  "+e.getMessage());
			}catch (IOException e) {
				// TODO Auto-generated catch block
				if(!checkFailedTimes.containsKey(machine.getId())){
					checkFailedTimes.put(machine.getId(), 1);
				}else{
					Integer testTimes = checkFailedTimes.get(machine.getId());
					checkFailedTimes.put(machine.getId(), testTimes+1);
					if(testTimes == 6){
						machine.setState(4);//for 4,define it offline
						((MachineDao) SpringContextWait.beanFcBeanFactory.getBean("machineDao")).updata(machine);
					}
				}
				e.printStackTrace();
				LogAppender.appendLog("error when check state for machine:"+machine.getId()+"   "+machine.getIpAddress()+"  "+e.getMessage());
			}
		}

	}
	public void chekState(){
		LogAppender.appendLog("server check state");
		//query database
		//..
		MachineDao machineDao = (MachineDao) SpringContextWait.beanFcBeanFactory.getBean("machineDao");
		if(machineDao == null){//software not start complement
			return;
		}

		List<Machine> machines = machineDao.listAll();
		for( Machine machine:machines){
			new StateCheckThread(machine).start();
		}
	}

	/*
	 * @return 
	 * 0.success
	 * 1.response data format exception
	 * 2.media not exist in machine
	 * 3.machine error
	 * 10.machine not exist
	 * 
	 */
	public int deleteMedia(int mediaID,int mechineID){
		//get host info
		String host = null;
		LogAppender.appendLog("delete a media:"+mediaID);
		MachineDao machineDao = (MachineDao) SpringContextWait.beanFcBeanFactory.getBean("machineDao");
		Machine machine = machineDao.getById(mechineID);
		if(machine == null){
			return 10;
		}

		host = machine.getIpAddress();
		if(host == null || "".equals(host)){
			return 9;
		}
		try {
			Socket soDeleteMedia = new Socket(host, 5000);
			MediaFileDeleteReq mediaFileDeleteReq = new MediaFileDeleteReq();
			mediaFileDeleteReq.setMediaID(mediaID);
			OutputStream osDeleteMedia = soDeleteMedia.getOutputStream();
			BufferedReader brDeleteMedia = new BufferedReader(new InputStreamReader(soDeleteMedia.getInputStream()));
			LogAppender.appendLog("send from server:"+JSONObject.fromObject(mediaFileDeleteReq).toString());
			osDeleteMedia.write(JSONObject.fromObject(mediaFileDeleteReq).toString().getBytes());

			//get response from machine
			StringBuilder sbosDeleteMedia = new StringBuilder();
			String temp = null;
			while((temp = brDeleteMedia.readLine())!= null){
				sbosDeleteMedia.append(temp);
			}
			brDeleteMedia.close();
			osDeleteMedia.close();
			soDeleteMedia.close();
			LogAppender.appendLog("recive from machine:"+sbosDeleteMedia);
			JSONObject jsonResponse = JSONObject.fromObject(sbosDeleteMedia);
			int machineResponse = jsonResponse.optInt("responseCode", -1);
			if(machineResponse == -1){
				return 1;
			}
			if(machineResponse == 2){
				return 2;
			}

			if(machineResponse == 1){
				return 3;
			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return 0;
	}

	public int setMeidaTime(int mediaID,int mechineID,int startTime,int endTime){

		//get host info
		String host = null;
		LogAppender.appendLog("setmedia Time:"+mediaID);
		MachineDao machineDao = (MachineDao) SpringContextWait.beanFcBeanFactory.getBean("machineDao");
		Machine machine = machineDao.getById(mechineID);
		if(machine == null){
			return 10;
		}

		host = machine.getIpAddress();
		if(host == null || "".equals(host)){
			return 9;
		}

		//get connect from host
		try {
			Socket somedia = new Socket(host, 50000);
			OutputStream osmeida = somedia.getOutputStream();
			BufferedReader brmedia = new BufferedReader(new InputStreamReader(somedia.getInputStream()));
			MediaTimeReq mediaTimeReq = new MediaTimeReq();
			mediaTimeReq.setStartTime(startTime);
			mediaTimeReq.setEndTime(endTime);
			mediaTimeReq.setMediaID(mediaID);
			LogAppender.appendLog("send from server:"+JSONObject.fromObject(mediaTimeReq).toString());
			osmeida.write(JSONObject.fromObject(mediaTimeReq).toString().getBytes());

			//get response from machine
			StringBuilder sbcheck = new StringBuilder();
			String temp = null;
			while((temp = brmedia.readLine())!= null){
				sbcheck.append(temp);
			}
			brmedia.close();
			osmeida.close();
			somedia.close();
			LogAppender.appendLog("revice form machine:"+temp);
			JSONObject jsonTimeResponse = JSONObject.fromObject(temp);
			if(jsonTimeResponse == null){
				return 1;
			}
			int timeResponseCode = jsonTimeResponse.optInt("responseCode", -1);
			if(timeResponseCode == -1){
				return 7;
			}
			if(timeResponseCode == 1){
				return 8;
			}
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return -1;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return -2;
		}
		return 0;
	}
	/*
	 * upload a media file to machine 
	 * 
	 * @return 
	 * -2.network exception
	 * -1.unknown host
	 * 0,successful
	 * 1.check response data format exception
	 * 2.media unavailable
	 * 3.file confirm response data format exception
	 * 4.file always exist ,update time
	 * 5.file upload response data format exception
	 * 6.file upload failed
	 * 7.time response data format exception
	 * 8.time set failed
	 * 9.machine info not complement
	 * 10.machine not exist
	 * 11.media not exist
	 */
	public int uploadMedia(int mediaID,int mechineID,int startTime,int endTime){
		//first step ,check machine function enable 
		String host = null;
		LogAppender.appendLog("upload media");
		//get host by machineID
		MediaEnableCheckReq enableCheckRequest = new MediaEnableCheckReq();
		MachineDao machineDao = (MachineDao) SpringContextWait.beanFcBeanFactory.getBean("machineDao");
		Machine machine = machineDao.getById(mechineID);
		if(machine == null){
			return 10;
		}

		host = machine.getIpAddress();
		if(host == null || "".equals(host)){
			return 9;
		}
		try {
			Socket socheck = new Socket(host, 50000);
			BufferedOutputStream oscheck = new BufferedOutputStream(socheck.getOutputStream());
			InputStream is  = socheck.getInputStream();
			LogAppender.appendLog("send from server:"+JSONObject.fromObject(enableCheckRequest).toString());
			oscheck.write(JSONObject.fromObject(enableCheckRequest).toString().getBytes());
			oscheck.flush();

			StringBuilder sb = new StringBuilder();
			byte[] space = new byte[1024];
			int n =0;
			while((n=is.read(space)) == 1024){
				sb.append(new String(space,0,n));
			}
			sb.append(new String(space,0,n));

			is.close();
			oscheck.close();
			socheck.close();

			LogAppender.appendLog("revice form machine:"+sb.toString());
			JSONObject jsonCheckResponse = JSONObject.fromObject(sb.toString());
			if(jsonCheckResponse == null){
				return 1;
			}
			int checkResponseCode = jsonCheckResponse.optInt("responseCode", -1);
			if(checkResponseCode == -1){
				return 1;
			}else if(checkResponseCode == 1){
				return 2;
			}

			// second step ,check file exist
			MediaFileConfirmReq mediaFileConfirmReq = new MediaFileConfirmReq();
			Socket sofile = new Socket(host,50000);
			OutputStream osfile = sofile.getOutputStream();
			is  = sofile.getInputStream();
			mediaFileConfirmReq.setMediaID(mediaID);
			LogAppender.appendLog("send from server:"+JSONObject.fromObject(mediaFileConfirmReq).toString());
			osfile.write(JSONObject.fromObject(mediaFileConfirmReq).toString().getBytes());

			sb = new StringBuilder();
			n =0;
			while((n=is.read(space)) == 1024){
				sb.append(new String(space,0,n));
			}
			sb.append(new String(space,0,n));

			is.close();
			osfile.close();
			sofile.close();
			LogAppender.appendLog("recive from machine:"+sb.toString());
			JSONObject jsonFileResponse = JSONObject.fromObject(sb.toString());
			if(jsonFileResponse == null){
				return 1;
			}
			int fileResponseCode = jsonCheckResponse.optInt("responseCode", -1);
			if(fileResponseCode == -1){
				return 3;
			}else if(fileResponseCode == 0){//upload file
				MediaDao mediaDao = (MediaDao) SpringContextWait.beanFcBeanFactory.getBean("mediaDao");
				Media media = mediaDao.getById(mediaID);
				if(media == null){
					return 11;
				}

				String path = media.getPath();
				if(path == null)
					return 11;
				File mediaFile = new File(path);
				if(!mediaFile.isFile()){
					return 11;
				}
				long n1 = mediaFile.length()/(4096);//a segment size 2M

				byte[] data = new byte[4096];
				FileInputStream fis = new FileInputStream(mediaFile);
				MediaFileUploadReq mediaFileUploadRequest = new MediaFileUploadReq();
				mediaFileUploadRequest.setFileName(mediaFile.getName());
				mediaFileUploadRequest.setMediaID(mediaID);

				
				mediaFileUploadRequest.setTotallength(mediaFile.length());

				ByteBuffer buffer = ByteBuffer.allocate(5096);
				
				for(int i =0;i<n1;i++){

					//buffer.clear();
					mediaFileUploadRequest.setOffset(i * (4096));
					Socket soMediaFile = new Socket(host, 50000);
					BufferedOutputStream osMediaFile = new BufferedOutputStream(soMediaFile.getOutputStream());

					int readlength = fis.read(data);
					mediaFileUploadRequest.setLength(readlength);
					//buffer.put((JSONObject.fromObject(mediaFileUploadRequest).toString().getBytes()));
					//buffer.put("\r\n\r\n".getBytes());
					//buffer.put(data);
					//System.out.println(new String(buffer.array()));
					//try{
					//	osMediaFile.write(buffer.);
					//	osMediaFile.flush();
					//}catch(Exception e){
					//	e.printStackTrace();
					//	System.out.println(e.getMessage());
					//}
					osMediaFile.write(JSONObject.fromObject(mediaFileUploadRequest).toString().getBytes());
					osMediaFile.write("\r\n\r\n".toString().getBytes());
					osMediaFile.write(data,0,readlength);
					osMediaFile.flush();
					
				//	osMediaFile.write((JSONObject.fromObject(mediaFileUploadRequest).toString()+"\r\n\r\n").getBytes());
					LogAppender.appendLog("send from server"+JSONObject.fromObject(mediaFileUploadRequest).toString());
					//write data
					
					//osMediaFile.write(data);

					//read response
					is = soMediaFile.getInputStream();
					sb = new StringBuilder();
					n =0;
					while((n=is.read(space)) == 1024){
						sb.append(new String(space,0,n));
					}
					sb.append(new String(space,0,n));
					//we don`t care the return data
					LogAppender.appendLog("revice to machine:"+sb.toString());

					is.close();
					osMediaFile.close();
					soMediaFile.close();
				}

				if(mediaFile.length() % (4096)!=0){//tail data
					mediaFileUploadRequest.setOffset(n * 4096);
					mediaFileUploadRequest.setLength(mediaFile.length() % (4096));
					Socket soMediaFile = new Socket(host, 5000);
					OutputStream osMediaFile = soMediaFile.getOutputStream();

					/*
					osMediaFile.write(JSONObject.fromObject(mediaFileUploadRequest).toString().getBytes());
					osMediaFile.write("\r\n\r\n".getBytes());//seperater like mail protocl
					fis.read(data);
					osMediaFile.write(data,0,(int)(mediaFile.length() % (2^21)));*/
					int n3 = fis.read(data);
					buffer.put((JSONObject.fromObject(mediaFileUploadRequest).toString().getBytes()));
					buffer.put("\r\n\r\n".getBytes());
					buffer.put(data,0,n3);
					//System.out.println(new String(buffer.array()));
					try{
						osMediaFile.write(buffer.array());
						osMediaFile.flush();
					}catch(Exception e){
						e.printStackTrace();
						System.out.println(e.getMessage());
					}
					

					//read response
					is = soMediaFile.getInputStream();
					sb = new StringBuilder();
					n =0;
					while((n=is.read(space)) == 1024){
						sb.append(new String(space,0,n));
					}
					sb.append(new String(space,0,n));
					//we don`t care the return data
					is.close();
					osMediaFile.close();
					soMediaFile.close();
				}

				fis.close();
			}
			if(startTime != endTime){//update time
				return setMeidaTime(mediaID, mechineID, startTime, endTime);
			}



		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return -1;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return -2;
		}

		return 0;
	}
}
